package process;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import indexing.*;

public class IndexProcess {
	
	private static Charset charset = Charset.forName("ISO8859_1");
	private static CharsetEncoder encoder = charset.newEncoder();
	private static CharsetDecoder decoder = charset.newDecoder();
	//Attributes used in the method "tagSentProcess()"
	private IndexSentence indexSentence;
	//Attributes used in the method "wordsIndexProcess()"
	private IndexWord indexWords;
	//Attributes used in the method "productsIndexProcess()"
	private IndexProducts indexProducts;
	/**
	 * This method receives a String returns the ByteBuffer that represents that String.
	 * @param String String to be transformed.
	 * @return ByteByffer ByteBuffer representation of the String. 
	 */
	public ByteBuffer stringToBBuffer(String msg){
		try{
		    return encoder.encode(CharBuffer.wrap(msg));
		  }catch(Exception e){e.printStackTrace();}
		  return null;
	}
	/**
	 * This method receives a ByteBuffer and returns the String that it represents.
	 * @param ByteBuffer ByteBuffer to be transformed.
	 * @return String String representation of the ByteBuffer. 
	 */
	public String BBuffertoString(ByteBuffer buffer){
		String data = "";
		  try{
		    int old_position = buffer.position();
		    data = decoder.decode(buffer).toString();
		    // reset buffer's position to its original so it is not altered:
		    buffer.position(old_position);  
		  }catch (Exception e){
		    e.printStackTrace();
		    return "";
		  }
		  return data;
	}
	/**
	 * This gets the current date.
	 * @param String Date format.
	 * @return String current Date. 
	 */
	public static String now(String dateFormat) {
	    Calendar cal = Calendar.getInstance();
	    SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
	    return sdf.format(cal.getTime());

	  }
	/**
	 * This method performs the tasks necessaries to index the Tagged Sentences document.
	 * @param String oriPath The path where the source file is located. In this case "SentenceSegmentation.txt"
	 * @param String indexDir The folder path where the index is going to be created. 
	 */
	public void tagSentProcess(String oriPath,String indexDir) throws FileNotFoundException,IOException,Exception{
		this.indexSentence=new IndexSentence();
		this.indexSentence.init(indexDir);
		File file;
		if(System.getProperty("os.name").toLowerCase().indexOf("win")>=0)
			 file= new File(oriPath+"\\SentenceSegmentation.txt");
		else
			file = new File(oriPath+"/SentenceSegmentation.txt");
		float fsize=file.length();
        /**
         * Initialization of class attributes
         */
        FileInputStream is = new FileInputStream(file);
        FileChannel f = is.getChannel();
        //Capacity of reading Buffer
        //1kb=1024 bytes ||MG=1024kb ()
        int inBufCap=5*1024*1024;//size in bytes
        ByteBuffer inBuf = ByteBuffer.allocateDirect(inBufCap);
        //Auxiliary Buffer to store the valid information
        ByteBuffer auxBuf=ByteBuffer.allocateDirect(inBufCap);
        char[] charArr;
        String s;
        long len = 0;
        int i=0;
        float i_filePosition=0,f_filePosition=0;
        //Storing initial time
        String initialTime=IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z");
        //
        while((len = f.read(inBuf)) != -1) {
                inBuf.flip();
                //Profiling code lines
                System.out.println("--Tagged Sentences Indexing--\nStarted:"+
                IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n");
                s=BBuffertoString(inBuf);
                /**
                 * Because the use of a fixed sized buffer it is possible to read incomplete instances, for example:
                 * "First buffer read"
                 * 		Instance_1  Field_1 Field_2 Part(Field_3)
                 * "Second buffer read"
                 * 		left_part(instance_1,Field_3)
                 * 		Instance_2  Field_1 Field_2 Field_3
                 * 
                 * So the lines written below pretend to analyze the information in the buffer an only take the information where
                 * the instances are complete. After the valid information was selected the position of the "filechannel" is reallocated
                 * in order to start from the last valid end point.
                 */
                //Transform the String information in the buffer, in a character array
                charArr=s.toCharArray();
                //Read the char array backwards
                for(i=charArr.length-1;i>-1;i--){
                	//Look for the last valid instance and used the other condition to avoid an infinite loop due to the reallocation
                	if(charArr[i]=='\n'&&(fsize-f.position())>=inBufCap){
                		//Extract the valid information
                		auxBuf=stringToBBuffer(s.substring(0, i));
                		//Calculate the new last valid position in the "filechannel"
                		f_filePosition=i_filePosition+auxBuf.limit();
                		//Reallocate
                		f.position((long)f_filePosition);
                		break;
                	}
                }
                //Clean the auxiliary variables
                charArr=null;
                //If it is the last chunk just write it, if it is analyzed as it was done previously you get a loop because the reallocation
                if((fsize-f.position())<inBufCap){
                	//Clean the auxiliary variables
                	auxBuf.clear();
                	//
                	this.indexSentence.generateInfoIndex(BBuffertoString(inBuf));
                }else{
                	//Clean the auxiliary variables
                	inBuf.clear();
                	//
                	this.indexSentence.generateInfoIndex(BBuffertoString(auxBuf));
                }
                //******Control Lines
        		//General purpose
                System.out.println("Left: "+String.valueOf((fsize-f.position()))+" from "+String.valueOf(fsize));
                System.out.println("Ended:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n******\n");
                //******
                inBuf.clear();
                //The line bellow stores the final valid position, where the file was read. 
                i_filePosition=f.position();
        }
        f.close();
        this.indexSentence.closeIndexWriter();
        //Time briefing
        System.out.println("******Briefing*******\nStart:"+initialTime+
        		"\n - End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z"));
	}
	/**
	 * this method receives a folder URL and return the files in the folder.
	 * @param String mainFolderPath The URL of the folder.
	 * @return File [] filesArray The list of files. 
	 */
	public File[] getFilesFromFolfer(String mainFolderPath){
		File folder=new File(mainFolderPath);
		ArrayList<File> filesList=new ArrayList<File>();
		int k=0;
		File [] auxFArray=folder.listFiles();
		for(k=0;k<auxFArray.length;k++){
			filesList.add(auxFArray[k]);
		}
		File [] filesArray=new File[filesList.size()];
		return filesList.toArray(filesArray);
	}
	/**
	 * This method performs the tasks necessaries to index the Good and Bad words archives.
	 * @param String oriPath The path where the source file is located. In this case the folder where the positive.txt and negativa.txt are located.
	 * @param String indexDir The folder path where the index is going to be created. 
	 */
	public void wordsIndexProcess(String oriPath,String indexDir) throws FileNotFoundException,IOException,Exception{
		this.indexWords=new IndexWord();
		this.indexWords.init(indexDir);
		float fsize;
        /**
         * Initialization of class attributes
         */
        FileInputStream is;
        FileChannel f;
        //Capacity of reading Buffer
        //1kb=1024 bytes ||MG=1024kb ()
        int inBufCap=5*1024*1024;//size in bytes
        ByteBuffer inBuf = ByteBuffer.allocateDirect(inBufCap);
        //Auxiliary Buffer to store the valid information
        ByteBuffer auxBuf=ByteBuffer.allocateDirect(inBufCap);
        char[] charArr;
        String s;
        long len = 0;
        int i=0;
        float i_filePosition=0,f_filePosition=0;
        //Storing initial time
        String initialTime=IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z");
        //
        File [] filesArray;
        if(System.getProperty("os.name").toLowerCase().indexOf("win")>=0)
        	filesArray=getFilesFromFolfer(oriPath+"\\word_class");
        else
        	filesArray=getFilesFromFolfer(oriPath+"/word_class");
        for(int w=0;w<filesArray.length;w++){
        	len=0;i=0;i_filePosition=0;f_filePosition=0;
        	is=new FileInputStream(filesArray[w]);
        	fsize=filesArray[w].length();
        	f = is.getChannel();
            while((len = f.read(inBuf)) != -1) {
                inBuf.flip();
                //Profiling code lines
                //System.out.println("--Tagged Sentences Archive--\nStarted:"+
                //IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n");
                s=BBuffertoString(inBuf);
                /**
                 * Because the use of a fixed sized buffer it is possible to read incomplete instances, for example:
                 * "First buffer read"
                 * 		Instance_1  Field_1 Field_2 Part(Field_3)
                 * "Second buffer read"
                 * 		left_part(instance_1,Field_3)
                 * 		Instance_2  Field_1 Field_2 Field_3
                 * 
                 * So the lines written below pretend to analyze the information in the buffer an only take the information where
                 * the instances are complete. After the valid information was selected the position of the "filechannel" is reallocated
                 * in order to start from the last valid end point.
                 */
                //Transform the String information in the buffer, in a character array
                charArr=s.toCharArray();
                //Read the char array backwards
                for(i=charArr.length-1;i>-1;i--){
                	//Look for the last valid instance and used the other condition to avoid an infinite loop due to the reallocation
                	if(charArr[i]=='\n'&&(fsize-f.position())>=inBufCap){
                		//Extract the valid information
                		auxBuf=stringToBBuffer(s.substring(0, i));
                		//Calculate the new last valid position in the "filechannel"
                		f_filePosition=i_filePosition+auxBuf.limit();
                		//Reallocate
                		f.position((long)f_filePosition);
                		break;
                	}
                }
                //Clean the auxiliary variables
                charArr=null;
                //If it is the last chunk just write it, if it is analyzed as it was done previously you get a loop because the reallocation
                if((fsize-f.position())<inBufCap){
                	//Clean the auxiliary variables
                	auxBuf.clear();
                	//
                	if(filesArray[w].getName().contains("positive")){
                		//Reading the good words archive
                		this.indexWords.addPositWord(this.indexWords.cleanInformation(BBuffertoString(inBuf)));
                	}else{
                		//Reading the bad words archive
                		this.indexWords.addNegtWord(this.indexWords.cleanInformation(BBuffertoString(inBuf)));
                	}
                }else{
                	//Clean the auxiliary variables
                	inBuf.clear();
                	//
                	if(filesArray[w].getName().contains("positive")){
                		//Reading the good words archive
                		this.indexWords.addPositWord(this.indexWords.cleanInformation(BBuffertoString(auxBuf)));
                	}else{
                		//Reading the bad words archive
                		this.indexWords.addNegtWord(this.indexWords.cleanInformation(BBuffertoString(auxBuf)));
                	}
                }
                //******Control Lines
        		//General purpose
                //System.out.println("Left: "+String.valueOf((fsize-f.position()))+" from "+String.valueOf(fsize));
                //System.out.println("Ended:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n******\n");
                //******
                inBuf.clear();
                //The line bellow stores the final valid position, where the file was read. 
                i_filePosition=f.position();
            }
            f.close();
        }
        this.indexWords.closeIndexWriter();
        //Time briefing
        System.out.println("******Briefing*******\nStart:"+initialTime+
        		"\n - End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z"));
	}
	/**
	 * This method performs the tasks necessaries to index the Tagged Sentences document.
	 * @param String oriPath The path where the source file is located. In this case "SentenceSegmentation.txt"
	 * @param String indexDir The folder path where the index is going to be created. 
	 * @throws Exception 
	 */
	public void productsIndexProcess(String oriPath,String indexDir) throws Exception{
		this.indexProducts=new IndexProducts();
		this.indexProducts.init(indexDir);
		File file;
		if(System.getProperty("os.name").toLowerCase().indexOf("win")>=0)
			file= new File(oriPath+"\\productinfo.txt");
		else
			file=new File(oriPath+"/productinfo.txt");
		float fsize=file.length();
        /**
         * Initialization of class attributes
         */
        FileInputStream is = new FileInputStream(file);
        FileChannel f = is.getChannel();
        //Capacity of reading Buffer
        //1kb=1024 bytes ||MG=1024kb ()
        int inBufCap=5*1024*1024;//size in bytes
        ByteBuffer inBuf = ByteBuffer.allocateDirect(inBufCap);
        //Auxiliary Buffer to store the valid information
        ByteBuffer auxBuf=ByteBuffer.allocateDirect(inBufCap);
        String s;
        long len = 0;
        int i=0;
        float i_filePosition=0,f_filePosition=0;
        //Storing initial time
        String initialTime=IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z");
        //
        while((len = f.read(inBuf)) != -1) {
                inBuf.flip();
                //Profiling code lines
                System.out.println("--Products Index Process --\nStarted:"+
                IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n");
                s=BBuffertoString(inBuf);
                /**
                 * Because the use of a fixed sized buffer it is possible to read incomplete instances, for example:
                 * "First buffer read"
                 * 		Instance_1  Field_1 Field_2 Part(Field_3)
                 * "Second buffer read"
                 * 		left_part(instance_1,Field_3)
                 * 		Instance_2  Field_1 Field_2 Field_3
                 * 
                 * So the lines written below pretend to analyze the information in the buffer an only take the information where
                 * the instances are complete. After the valid information was selected the position of the "filechannel" is reallocated
                 * in order to start from the last valid end point.
                 */
                if((fsize-f.position())>=inBufCap){
                	//Look for the last valid instance
                	i=s.lastIndexOf("BREAK-REVIEWED");
                	//Extract the valid information
                	auxBuf=stringToBBuffer(s.substring(0,i));
                	//Calculate the new last valid position in the "filechannel"
                	f_filePosition=i_filePosition+auxBuf.limit();
                	f.position((long)f_filePosition);
                }
                //If it is the last chunk just write it, if it is analyzed as it was done previously you get a loop because the reallocation
                if((fsize-f.position())<inBufCap){
                	//Clean the auxiliary variables
                	auxBuf.clear();
                	//
                	this.indexProducts.generateProductIndex(BBuffertoString(inBuf));
                }else{
                	//Clean the auxiliary variables
                	inBuf.clear();
                	//
                	this.indexProducts.generateProductIndex(BBuffertoString(auxBuf));
                }
                //******Control Lines
        		//General purpose
                System.out.println("Left: "+String.valueOf((fsize-f.position()))+" from "+String.valueOf(fsize));
                System.out.println("Ended:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n******\n");
                //******
                inBuf.clear();
                //The line bellow stores the final valid position, where the file was read. 
                i_filePosition=f.position();
        }
        f.close();
        this.indexSentence.closeIndexWriter();
        //Time briefing
        System.out.println("******Briefing*******\nStart:"+initialTime+
        		"\n - End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z"));
	}
	
	
	public static void main(String[] args) throws IOException,Exception{
		//When you execute the program in the console the parameters bust be sent as: 
		//java -Xmx800m -jar run_indexTAMB.jar index_Directory tagSentece_Directory_Location words_Directory_Location products_Directory_Location 
		//String indexDir="C:\\Proyecto\\indexs";
		String indexDir=args[0];
		//String tagSentOP="C:\\Proyecto\\results";
		String tagSentOP=args[1];
		//String wordsOP="C:\\Proyecto\\word_class";
		String wordsOP=args[2];
		//String productsOP="C:\\Proyecto";
		String productsOP=args[3];
		IndexProcess indexProcess=new IndexProcess();
		//Control statistics file
		String statisticLine="";
		FileWriter fstream;
		if(System.getProperty("os.name").toLowerCase().indexOf("win")>=0)
			fstream=new FileWriter(tagSentOP+"\\statistics.txt",true);
		else
			fstream=new FileWriter(tagSentOP+"/statistics.txt",true);
		BufferedWriter out = new BufferedWriter(fstream);
		//
		statisticLine="--Tagged Sentences Indexing --\nStarted:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n";
		out.write(statisticLine);
		//indexProcess.tagSentProcess(tagSentOP, indexDir);
		statisticLine="End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n"+"_______________________________________________"+"\n";
		out.write(statisticLine);
		//
		statisticLine="--Positive and Negative words Indexing --\nStarted:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n";
		out.write(statisticLine);
		//indexProcess.wordsIndexProcess(wordsOP, indexDir);
		statisticLine="End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n"+"_______________________________________________"+"\n";
		out.write(statisticLine);
		//
		statisticLine="--Products Index Process --\nStarted:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n";
		out.write(statisticLine);
		indexProcess.productsIndexProcess(productsOP, indexDir);
		statisticLine="End:"+IndexProcess.now("yyyy.MM.dd G 'at' hh:mm:ss z")+"\n"+"_______________________________________________"+"\n";
		out.write(statisticLine);
		out.close();		
	}

}
